Selecting an adequate dataset to analyze was quite interesting and after some research I came aross a dataset regarding Boston Crime data on kaggle.com. The dataset’s records begin in June 14, 2015 and continue to September 3, 2018 making it quite a large table. The data was provided as a .csv file therfore I imported the data using the read.csv() function. After importing the datasets, I used quick presentations functions to take a glance at the data. Once all of the data is loaded I formulated several questions that quided the analysis: 1) How has crime changed over the years? 2) Is it possible to predict where or when a crime will be committed? 3) Which areas of the city have evolved over this time span? 4) In which area(s) have most of the crimes been committed? 5) What kind of affect did the legalization of marijuana have on drug related crimes within Boston?

crime = read.csv("crime.csv")
offense_codes = read.csv("offense_codes.csv")
library(DataComputing)
Loading required package: dplyr
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     

Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff,
    setequal, union

Loading required package: ggplot2
Need help? Try Stackoverflow:
https://stackoverflow.com/tags/ggplot2.
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'mosaic':
  method                           from   
  fortify.SpatialPolygonsDataFrame ggplot2
library(mosaic)
Loading required package: lattice
Loading required package: ggformula
Loading required package: ggstance

Attaching package: 㤼㸱ggstance㤼㸲

The following objects are masked from 㤼㸱package:ggplot2㤼㸲:

    geom_errorbarh,
    GeomErrorbarh


New to ggformula?  Try the tutorials: 
    learnr::run_tutorial("introduction", package = "ggformula")
    learnr::run_tutorial("refining", package = "ggformula")
Loading required package: mosaicData
Loading required package: Matrix

The 'mosaic' package masks several functions from core packages in order to add 
additional features.  The original behavior of these functions should not be affected by this.

Note: If you use the Matrix package, be sure to load it BEFORE loading mosaic.

Attaching package: 㤼㸱mosaic㤼㸲

The following object is masked from 㤼㸱package:Matrix㤼㸲:

    mean

The following object is masked from 㤼㸱package:ggplot2㤼㸲:

    stat

The following objects are masked from 㤼㸱package:dplyr㤼㸲:

    count, do, tally

The following objects are masked from 㤼㸱package:stats㤼㸲:

    binom.test, cor, cor.test,
    cov, fivenum, IQR, median,
    prop.test, quantile, sd,
    t.test, var

The following objects are masked from 㤼㸱package:base㤼㸲:

    max, mean, min, prod, range,
    sample, sum
library(devtools)
Loading required package: usethis
library(leaflet)
library(ggplot2)
library(gganimate)
library(tidyverse)
-- Attaching packages --------------------------------------- tidyverse 1.2.1 --
<U+2713> tibble  2.1.3     <U+2713> purrr   0.3.3
<U+2713> tidyr   0.8.3     <U+2713> stringr 1.4.0
<U+2713> readr   1.3.1     <U+2713> forcats 0.4.0
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x mosaic::count()            masks dplyr::count()
x purrr::cross()             masks mosaic::cross()
x mosaic::do()               masks dplyr::do()
x tidyr::expand()            masks Matrix::expand()
x dplyr::filter()            masks stats::filter()
x ggstance::geom_errorbarh() masks ggplot2::geom_errorbarh()
x dplyr::lag()               masks stats::lag()
x mosaic::stat()             masks ggplot2::stat()
x mosaic::tally()            masks dplyr::tally()
library(party)
Loading required package: grid
Loading required package: mvtnorm
Loading required package: modeltools
Loading required package: stats4
Loading required package: strucchange
Loading required package: zoo

Attaching package: 㤼㸱zoo㤼㸲

The following objects are masked from 㤼㸱package:base㤼㸲:

    as.Date, as.Date.numeric

Loading required package: sandwich

Attaching package: 㤼㸱strucchange㤼㸲

The following object is masked from 㤼㸱package:stringr㤼㸲:

    boundary
library(lubridate)

Attaching package: 㤼㸱lubridate㤼㸲

The following object is masked from 㤼㸱package:base㤼㸲:

    date
head(crime)
nrow(crime)
[1] 319073
head(offense_codes)

DATA WRANGLING

At first glance, I noticed that the column titled “SHOOTING” had missing data and I wanted to see how much of the column was missing before beginning the full statistical analysis. Below shows that the SHOOTING column actually has the same amount of missing data as number of rows in the dataset.

sum(is.na(crime$SHOOTING))
[1] 0
crime <-
  crime %>%
  transform(OFFENSE_CODE_GROUP = as.factor(OFFENSE_CODE_GROUP))%>%
  transform(OCCURRED_ON_DATE = as.Date(OCCURRED_ON_DATE))%>%
  #transform(HOUR = as.integer(HOUR))%>%
  transform(OFFENSE_CODE = as.character(OFFENSE_CODE))

crime  
offense_codes <- offense_codes %>%
  transform(CODE = as.character(CODE))
offense_codes
NA
NA

This dataset contains three main categories in which its variables can be divided into for further analysis. These three categories are found below: starting with variables related to location,

locationData <-
  crime %>%
  select(STREET, Long, Lat, Location, DISTRICT, REPORTING_AREA, OFFENSE_CODE_GROUP) %>%
  na.omit()
#time data

timeData <-
  crime %>%
  select(OCCURRED_ON_DATE,HOUR, DAY_OF_WEEK,MONTH, YEAR, OFFENSE_CODE_GROUP) %>%
  na.omit()
# crime data
crime_data <-
  crime %>%
  select(INCIDENT_NUMBER, OFFENSE_CODE, OFFENSE_CODE_GROUP, OFFENSE_DESCRIPTION, UCR_PART) %>%
  na.omit()

how many crimes happen per day?

day_of_week_table <-
  crime %>% 
  group_by(DAY_OF_WEEK) %>%
  summarise(num_occurances = n()) %>%
  arrange(desc(num_occurances)) 
day_of_week_table
NA
NA

number of each crime committed

top_5_crimes <-
crime_data %>%
  group_by(OFFENSE_CODE_GROUP) %>%
  summarise(total = n()) %>%
  arrange(desc(total))%>%
  head(5)

top_5_crimes
crime_data %>%
  group_by(OFFENSE_CODE_GROUP)%>%
  summarise(total = n()) %>%
  arrange(desc(total)) %>%
  head(10)

how many violent crimes happen per street?

violent_crimes <-
  crime %>%
  filter(UCR_PART == "Part One") %>%
  group_by(OFFENSE_CODE_GROUP) %>%
  summarise(count = n()) %>%
  arrange(desc(count))
violent_crimes

crime_by_district <-
  crime %>% 
  group_by(DISTRICT) %>%
  summarise(count = n())%>%
  mutate(percent= round(count/sum(count) * 100)) %>%
  arrange(desc(percent))

Visualization Of the crime frequency per distrct frequency of crime per distrcit

ggplot(subset(crime,!is.na(DISTRICT)))+
  aes(x=DISTRICT)+
  geom_bar(stat = "count",fill='blue') + 
  labs(title="How many crimes occur by district?",
       x="District",
       y="Number of Crimes")

ggplot(data = crime_by_district,aes(x = DISTRICT, y = count)) +
  geom_point(aes(size = percent))

Below shows the number of each individual crime that occur each year

crime_types <- 
  crime %>%
  group_by(OFFENSE_CODE_GROUP, YEAR) %>%
  mutate(total = n()) %>%
  distinct(OFFENSE_CODE_GROUP, total)%>%
  arrange(YEAR)
crime_types

Investigating drug related crimes

drugCrimes <- 
  crime %>%
  group_by(OFFENSE_CODE_GROUP , YEAR, MONTH)%>%
  mutate(total = n()) %>%
  distinct(OFFENSE_CODE_GROUP, total)%>%
  arrange(YEAR, MONTH) %>%
  filter(OFFENSE_CODE_GROUP == "Drug Violation") %>% 
  transform(YEAR = as.character(YEAR))

drugCrimes  
# joining the offense code table with drug table

crimeJoin <-
  dplyr::left_join(crime, offense_codes, by = c("OFFENSE_CODE"= "CODE")) 
crimeJoin
NA

Due to the fact that the legalization of marijuana has become quite a trend throughtout the US, I was curious of what effect it had in Boston. Below shows the number of drug related charges and you see a significant drop around mid 2016 becasue marijuana was legalized in July of 2016.


crime_types %>%
  filter(OFFENSE_CODE_GROUP %in% c("Drug Violation")) %>%
ggplot(aes(x = YEAR, y = total)) +
  geom_line() 

Here is a graph showing the significant drop in drug related charges with an x-intercept at the time when marijuana was legalized.


drugCrimes %>%
  filter(YEAR == "2016") %>%
  ggplot(aes(x = MONTH , y = total)) +
  geom_line() +
  geom_smooth() +
  geom_vline(xintercept = 7)


crime %>%
  filter(str_detect(OFFENSE_CODE_GROUP, "^Drug")) %>%
  filter(OCCURRED_ON_DATE <= ymd("2016-12-30")) %>%
  filter(OCCURRED_ON_DATE > ymd("2016-01-01")) %>%
  count(OCCURRED_ON_DATE) %>%
  ggplot(aes(OCCURRED_ON_DATE, n))+
  geom_line() 

when comparing the two graphs above you see how the legalization of marijuanaa has affected the drug crime in boston

Relationship between crime occurances and time

crimes_by_year <-
  crime %>% 
  group_by(YEAR) %>%
  summarise(count = n())
crimes_by_year 
NA
NA
NA
#wide format of number of crimes per year
crimes_by_year %>%
  spread(key= YEAR, value = count)
crime %>% 
  
  count(OCCURRED_ON_DATE) %>% 
  ggplot(aes(OCCURRED_ON_DATE, n))+
  geom_line()+ 
  geom_smooth()+
  labs(title ="Crime Timeline 06/14/15 - 09/03/18", x = "Year", y ="Number of Crimes", color = "red")

According to the timeseries above crime trends follow a consistent trend with dips on number of occurances at the end of each year.

crimes_by_month <-
  crime %>% 
  group_by(MONTH) %>%
  summarise(count = n())
  
crime %>%
  ggplot(aes(x=DAY_OF_WEEK)) + 
  geom_bar(fill="gray", adjust = 2)
Ignoring unknown parameters: adjust

Visualization of Crimes by Day of and UCT_PART


  ggplot(crime, aes(x= DAY_OF_WEEK, fill = UCR_PART)) +
  geom_bar() +
  theme_bw() +
  labs( x = "Day of the week") 

NA
NA
ggplot(crime,aes( x= MONTH )) + 
  geom_bar() +
  ggtitle("Crimes by Month")

ggplot(data = crime, aes(x= YEAR, fill = UCR_PART )) +
  geom_histogram()

crime %>%
  group_by(OFFENSE_CODE_GROUP, STREET) %>%
  summarise(total = n()) %>%
  arrange(desc(total))
topCrimes <- 
  crime %>%
  group_by(OFFENSE_CODE_GROUP)%>%
  summarise(total = n())%>%
  filter(total >= 18075) %>%
  arrange(desc(total))
 
 
streets <-
  crime %>%
  group_by(OFFENSE_CODE_GROUP, STREET) %>%
  summarise(total = n()) %>%
  arrange(desc(total))
pattern <- "(STREET| ROAD|ST|STREET|RD|HIGHWAY|HWY|BYPASS|PIKE|TURNPIKE|AVE|AVENUE|BOULEVARD|BLVD)"

crimesOnMajorRoad <-streets %>%
  filter(grepl(pattern, STREET))
crimesOnMajorRoad

how many crimes happen on each day?

day_counts <-
  crime %>%
  group_by(OCCURRED_ON_DATE, DAY_OF_WEEK) %>%
  mutate(count = n()) %>%
  arrange((OCCURRED_ON_DATE)) %>%
  select(DAY_OF_WEEK,OCCURRED_ON_DATE,count) %>%
  distinct(DAY_OF_WEEK,OCCURRED_ON_DATE,count)
day_counts
NA
library(viridis)
Loading required package: viridisLite
library(hrbrthemes)
Registering Windows fonts with R
NOTE: Either Arial Narrow or Roboto Condensed fonts are required to use these themes.
      Please use hrbrthemes::import_roboto_condensed() to install Roboto Condensed and
      if Arial Narrow is not on your system, please see http://bit.ly/arialnarrow
library(plotly)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

Attaching package: 㤼㸱plotly㤼㸲

The following object is masked from 㤼㸱package:mosaic㤼㸲:

    do

The following object is masked from 㤼㸱package:ggplot2㤼㸲:

    last_plot

The following object is masked from 㤼㸱package:stats㤼㸲:

    filter

The following object is masked from 㤼㸱package:graphics㤼㸲:

    layout
time <- 
# Load dataset from github
df <- crime_types %>%
  filter(OFFENSE_CODE_GROUP %in% c("Motor Vehicle Accident Response", "Medical Assistance", "Investigate Person","Larceny", "Residential Burglary"))


# Graph
p <- df %>% 
  ggplot( aes(x=YEAR, y=total, fill=OFFENSE_CODE_GROUP, text=OFFENSE_CODE_GROUP)) +
    geom_area( ) +
  scale_fill_viridis(discrete = TRUE) +
    theme(legend.position="none") +
    ggtitle("How many of the top 5 crimes occur over the years") +
    theme_ipsum() +
    theme(legend.position="none")
p <- ggplotly(p, tooltip = "text")
p

NA
NA
#crime by district
  crime %>% 
  
  group_by(DISTRICT) %>%
  summarise(count = n())%>%
  mutate(percent= round(count/sum(count) * 100)) %>%
  arrange(desc(percent))
#crime by district at certain long lat, however its repeating the count
xyz <-
  crime %>% 
  
  group_by(DISTRICT) %>%
  mutate(count = n())%>%
  mutate(percent= round(count/sum(count) * 100)) %>%
  #arrange(desc(percent))%>%
  select(DISTRICT, count,Lat, Long)
 
xyz %>%
  group_by(DISTRICT, count)%>%
  distinct() %>%
  filter(str_detect(Lat, ""))
NA
NA
NA

MAPPING

Given that this dataset contained location data pertaining to each reported incident I thought that the leaflet package would be vital in illustrating where crimes occured throughout Boston.

The first map that I used was one that shows where on a map the Boston is located.

leaflet() %>%
  addTiles() %>%
  addMarkers(lng=-71.057, lat=42.361, popup="Boston, MA") %>%
  setView(lng=-71.057, lat=42.361, zoom = 10)

kidnapping distance calculations

kidnap <-
  crime %>%
  select(INCIDENT_NUMBER,STREET,OFFENSE_CODE,OFFENSE_CODE_GROUP, OFFENSE_DESCRIPTION, Long, Lat, Location, OCCURRED_ON_DATE)%>%
  filter(OFFENSE_DESCRIPTION %in% c("MISSING PERSON")) %>%
  arrange(OCCURRED_ON_DATE)


found <-
  crime %>%
  select(INCIDENT_NUMBER,STREET,OFFENSE_CODE,OFFENSE_CODE_GROUP, OFFENSE_DESCRIPTION, Long, Lat, Location, OCCURRED_ON_DATE)%>%
  filter(OFFENSE_DESCRIPTION %in% c("MISSING PERSON - LOCATED")) %>%
  arrange(OCCURRED_ON_DATE) %>%
  rename(Lat2 = Lat, Long2 = Long )
kidnapPairs <-
  merge(data.frame(kidnap, row.names = NULL), data.frame(found, row.names = NULL), by = 0, all = TRUE)[-1]
crime %>%
  select(INCIDENT_NUMBER,STREET,OFFENSE_CODE,OFFENSE_CODE_GROUP, OFFENSE_DESCRIPTION, Long, Lat, Location, OCCURRED_ON_DATE)%>%
  #filter(OFFENSE_DESCRIPTION %in% c("MISSING PERSON"))%>%
  mutate(missing = OFFENSE_CODE_GROUP == "Missing Person Reported") %>%
  filter(OFFENSE_CODE_GROUP %in% c("Missing Person Reported", "Missing Person Located"))%>%
  ggplot(aes(x = OFFENSE_CODE_GROUP, fill = missing)) +
  geom_bar()

This barplot illustrates that there are more missing people located than are reported which is quite interresting given the police aren’t aware of many of these missing persons.

Below shows the locations of all repoted kidnappings/locations of where victims were found. The red circles indicate the kidnapping location and the blue indicates the locations where missing people were found

kidnapMap <-
  leaflet(kidnapPairs)%>%
  addTiles() %>%
  addCircles(lat = ~Lat, lng = ~Long, radius = 2, color= "red")%>%
  addCircles(lat = ~Lat2, lng = ~Long2, radius = 2, color= "blue") %>%
  setView(-71.05, 42.36, zoom = 12)
Data contains 649 rows with either missing or invalid lat/lon values and will be ignoredData contains 71 rows with either missing or invalid lat/lon values and will be ignored
kidnapMap

NA

Locations of all homicides in boston

crimeJoin %>%
  group_by(OFFENSE_CODE_GROUP,DAY_OF_WEEK, HOUR, UCR_PART )%>%
  filter(OFFENSE_CODE_GROUP %in% c("Homicide")) %>% 
  leaflet() %>%
  addTiles() %>%
  addCircleMarkers(radius = 2, color = "red") %>%
  setView(-71.05, 42.36, zoom = 12)
Assuming "Long" and "Lat" are longitude and latitude, respectively
Data contains 16 rows with either missing or invalid lat/lon values and will be ignored

NA

Conclusion

After analyzing the Boston Crime Data Set, we can clearly see the trends and relations between the types of crimes, location and the time of the crime. Some of the notable takeaways from the analysis are that most crimes in Boston occurred on a Wednesday and during the month of October. The most affected districts in Boston are Dorchester(C11), South-End, Roxbury(B2). It can be noted that the highest number of the crimes were reported during summer months of July and Auguest. Motor-Vehicle accident response were the highest number of report registered with the Boston Police.

LS0tDQp0aXRsZTogIlNUQVQgMTg0IEZpbmFsIHByb2plY3QiIA0Kc3VidGl0bGU6ICJCb3N0b24gQ3JpbWUgRGF0YXNldCINCmF1dGhvcjogIktlZW5hbiBIYXJsZXN0b24iDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KU2VsZWN0aW5nIGFuIGFkZXF1YXRlIGRhdGFzZXQgdG8gYW5hbHl6ZSB3YXMgcXVpdGUgaW50ZXJlc3RpbmcgYW5kIGFmdGVyIHNvbWUgcmVzZWFyY2ggSSBjYW1lIGFyb3NzIGEgZGF0YXNldCByZWdhcmRpbmcgQm9zdG9uIENyaW1lIGRhdGEgb24ga2FnZ2xlLmNvbS4gVGhlIGRhdGFzZXQncyByZWNvcmRzIGJlZ2luIGluIEp1bmUgMTQsIDIwMTUgYW5kIGNvbnRpbnVlIHRvIFNlcHRlbWJlciAzLCAyMDE4IG1ha2luZyBpdCBxdWl0ZSBhIGxhcmdlIHRhYmxlLiBUaGUgZGF0YSB3YXMgcHJvdmlkZWQgYXMgYSAuY3N2IGZpbGUgdGhlcmZvcmUgSSBpbXBvcnRlZCB0aGUgZGF0YSB1c2luZyB0aGUgcmVhZC5jc3YoKSBmdW5jdGlvbi4gQWZ0ZXIgaW1wb3J0aW5nIHRoZSBkYXRhc2V0cywgSSB1c2VkIHF1aWNrIHByZXNlbnRhdGlvbnMgZnVuY3Rpb25zIHRvIHRha2UgYSBnbGFuY2UgYXQgdGhlIGRhdGEuIE9uY2UgYWxsIG9mIHRoZSBkYXRhIGlzIGxvYWRlZCBJIGZvcm11bGF0ZWQgc2V2ZXJhbCBxdWVzdGlvbnMgdGhhdCBxdWlkZWQgdGhlIGFuYWx5c2lzOiAxKSBIb3cgaGFzIGNyaW1lIGNoYW5nZWQgb3ZlciB0aGUgeWVhcnM/IDIpIElzIGl0IHBvc3NpYmxlIHRvIHByZWRpY3Qgd2hlcmUgb3Igd2hlbiBhIGNyaW1lIHdpbGwgYmUgY29tbWl0dGVkPyAzKSBXaGljaCBhcmVhcyBvZiB0aGUgY2l0eSBoYXZlIGV2b2x2ZWQgb3ZlciB0aGlzIHRpbWUgc3Bhbj8gNCkgSW4gd2hpY2ggYXJlYShzKSBoYXZlIG1vc3Qgb2YgdGhlIGNyaW1lcyBiZWVuIGNvbW1pdHRlZD8gNSkgV2hhdCBraW5kIG9mIGFmZmVjdCBkaWQgdGhlIGxlZ2FsaXphdGlvbiBvZiBtYXJpanVhbmEgaGF2ZSBvbiBkcnVnIHJlbGF0ZWQgY3JpbWVzIHdpdGhpbiBCb3N0b24/DQoNCmBgYHtyfQ0KY3JpbWUgPSByZWFkLmNzdigiY3JpbWUuY3N2IikNCm9mZmVuc2VfY29kZXMgPSByZWFkLmNzdigib2ZmZW5zZV9jb2Rlcy5jc3YiKQ0KbGlicmFyeShEYXRhQ29tcHV0aW5nKQ0KbGlicmFyeShtb3NhaWMpDQpsaWJyYXJ5KGRldnRvb2xzKQ0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShnZ2FuaW1hdGUpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocGFydHkpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmhlYWQoY3JpbWUpDQpucm93KGNyaW1lKQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChvZmZlbnNlX2NvZGVzKQ0KYGBgDQoNCiMgREFUQSBXUkFOR0xJTkcNCkF0IGZpcnN0IGdsYW5jZSwgSSBub3RpY2VkIHRoYXQgdGhlIGNvbHVtbiB0aXRsZWQgIlNIT09USU5HIiBoYWQgbWlzc2luZyBkYXRhIGFuZCBJIHdhbnRlZCB0byBzZWUgaG93IG11Y2ggb2YgdGhlIGNvbHVtbiB3YXMgbWlzc2luZyBiZWZvcmUgYmVnaW5uaW5nIHRoZSBmdWxsIHN0YXRpc3RpY2FsIGFuYWx5c2lzLiBCZWxvdyBzaG93cyB0aGF0ICB0aGUgU0hPT1RJTkcgY29sdW1uIGFjdHVhbGx5IGhhcyB0aGUgc2FtZSBhbW91bnQgb2YgbWlzc2luZyBkYXRhIGFzIG51bWJlciBvZiByb3dzIGluIHRoZSBkYXRhc2V0Lg0KYGBge3J9DQpzdW0oaXMubmEoY3JpbWUkU0hPT1RJTkcpKQ0KYGBgDQoNCg0KYGBge3J9DQpjcmltZSA8LQ0KICBjcmltZSAlPiUNCiAgdHJhbnNmb3JtKE9GRkVOU0VfQ09ERV9HUk9VUCA9IGFzLmZhY3RvcihPRkZFTlNFX0NPREVfR1JPVVApKSU+JQ0KICB0cmFuc2Zvcm0oT0NDVVJSRURfT05fREFURSA9IGFzLkRhdGUoT0NDVVJSRURfT05fREFURSkpJT4lDQogICN0cmFuc2Zvcm0oSE9VUiA9IGFzLmludGVnZXIoSE9VUikpJT4lDQogIHRyYW5zZm9ybShPRkZFTlNFX0NPREUgPSBhcy5jaGFyYWN0ZXIoT0ZGRU5TRV9DT0RFKSkNCg0KY3JpbWUgIA0Kb2ZmZW5zZV9jb2RlcyA8LSBvZmZlbnNlX2NvZGVzICU+JQ0KICB0cmFuc2Zvcm0oQ09ERSA9IGFzLmNoYXJhY3RlcihDT0RFKSkNCm9mZmVuc2VfY29kZXMNCg0KDQpgYGANClRoaXMgZGF0YXNldCBjb250YWlucyB0aHJlZSBtYWluIGNhdGVnb3JpZXMgaW4gd2hpY2ggaXRzIHZhcmlhYmxlcyBjYW4gYmUgZGl2aWRlZCBpbnRvIGZvciBmdXJ0aGVyIGFuYWx5c2lzLiBUaGVzZSB0aHJlZSBjYXRlZ29yaWVzIGFyZSBmb3VuZCBiZWxvdzogc3RhcnRpbmcgd2l0aCB2YXJpYWJsZXMgcmVsYXRlZCB0byBsb2NhdGlvbiwNCmBgYHtyfQ0KbG9jYXRpb25EYXRhIDwtDQogIGNyaW1lICU+JQ0KICBzZWxlY3QoU1RSRUVULCBMb25nLCBMYXQsIExvY2F0aW9uLCBESVNUUklDVCwgUkVQT1JUSU5HX0FSRUEsIE9GRkVOU0VfQ09ERV9HUk9VUCkgJT4lDQogIG5hLm9taXQoKQ0KDQpgYGANCg0KYGBge3J9DQojdGltZSBkYXRhDQoNCnRpbWVEYXRhIDwtDQogIGNyaW1lICU+JQ0KICBzZWxlY3QoT0NDVVJSRURfT05fREFURSxIT1VSLCBEQVlfT0ZfV0VFSyxNT05USCwgWUVBUiwgT0ZGRU5TRV9DT0RFX0dST1VQKSAlPiUNCiAgbmEub21pdCgpDQpgYGANCg0KYGBge3J9DQojIGNyaW1lIGRhdGENCmNyaW1lX2RhdGEgPC0NCiAgY3JpbWUgJT4lDQogIHNlbGVjdChJTkNJREVOVF9OVU1CRVIsIE9GRkVOU0VfQ09ERSwgT0ZGRU5TRV9DT0RFX0dST1VQLCBPRkZFTlNFX0RFU0NSSVBUSU9OLCBVQ1JfUEFSVCkgJT4lDQogIG5hLm9taXQoKQ0KYGBgDQoNCg0KaG93IG1hbnkgY3JpbWVzIGhhcHBlbiBwZXIgZGF5Pw0KYGBge3J9DQpkYXlfb2Zfd2Vla190YWJsZSA8LQ0KICBjcmltZSAlPiUgDQogIGdyb3VwX2J5KERBWV9PRl9XRUVLKSAlPiUNCiAgc3VtbWFyaXNlKG51bV9vY2N1cmFuY2VzID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKG51bV9vY2N1cmFuY2VzKSkgDQpkYXlfb2Zfd2Vla190YWJsZQ0KDQoNCmBgYA0KbnVtYmVyIG9mIGVhY2ggY3JpbWUgY29tbWl0dGVkDQpgYGB7cn0NCnRvcF81X2NyaW1lcyA8LQ0KY3JpbWVfZGF0YSAlPiUNCiAgZ3JvdXBfYnkoT0ZGRU5TRV9DT0RFX0dST1VQKSAlPiUNCiAgc3VtbWFyaXNlKHRvdGFsID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsKSklPiUNCiAgaGVhZCg1KQ0KDQp0b3BfNV9jcmltZXMNCmNyaW1lX2RhdGEgJT4lDQogIGdyb3VwX2J5KE9GRkVOU0VfQ09ERV9HUk9VUCklPiUNCiAgc3VtbWFyaXNlKHRvdGFsID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkgJT4lDQogIGhlYWQoMTApDQpgYGANCg0KaG93IG1hbnkgdmlvbGVudCBjcmltZXMgaGFwcGVuIHBlciBzdHJlZXQ/DQpgYGB7cn0NCnZpb2xlbnRfY3JpbWVzIDwtDQogIGNyaW1lICU+JQ0KICBmaWx0ZXIoVUNSX1BBUlQgPT0gIlBhcnQgT25lIikgJT4lDQogIGdyb3VwX2J5KE9GRkVOU0VfQ09ERV9HUk9VUCkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhjb3VudCkpDQp2aW9sZW50X2NyaW1lcw0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KDQpjcmltZV9ieV9kaXN0cmljdCA8LQ0KICBjcmltZSAlPiUgDQogIGdyb3VwX2J5KERJU1RSSUNUKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSU+JQ0KICBtdXRhdGUocGVyY2VudD0gcm91bmQoY291bnQvc3VtKGNvdW50KSAqIDEwMCkpICU+JQ0KICBhcnJhbmdlKGRlc2MocGVyY2VudCkpDQoNCg0KYGBgDQpWaXN1YWxpemF0aW9uIE9mIHRoZSBjcmltZSBmcmVxdWVuY3kgcGVyIGRpc3RyY3QgDQpmcmVxdWVuY3kgb2YgY3JpbWUgcGVyIGRpc3RyY2l0DQpgYGB7cn0NCmdncGxvdChzdWJzZXQoY3JpbWUsIWlzLm5hKERJU1RSSUNUKSkpKw0KICBhZXMoeD1ESVNUUklDVCkrDQogIGdlb21fYmFyKHN0YXQgPSAiY291bnQiLGZpbGw9J2JsdWUnKSArIA0KICBsYWJzKHRpdGxlPSJIb3cgbWFueSBjcmltZXMgb2NjdXIgYnkgZGlzdHJpY3Q/IiwNCiAgICAgICB4PSJEaXN0cmljdCIsDQogICAgICAgeT0iTnVtYmVyIG9mIENyaW1lcyIpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBjcmltZV9ieV9kaXN0cmljdCxhZXMoeCA9IERJU1RSSUNULCB5ID0gY291bnQpKSArDQogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBwZXJjZW50KSkNCg0KYGBgDQoNCg0KQmVsb3cgc2hvd3MgdGhlIG51bWJlciBvZiBlYWNoIGluZGl2aWR1YWwgY3JpbWUgdGhhdCBvY2N1ciBlYWNoIHllYXINCmBgYHtyfQ0KY3JpbWVfdHlwZXMgPC0gDQogIGNyaW1lICU+JQ0KICBncm91cF9ieShPRkZFTlNFX0NPREVfR1JPVVAsIFlFQVIpICU+JQ0KICBtdXRhdGUodG90YWwgPSBuKCkpICU+JQ0KICBkaXN0aW5jdChPRkZFTlNFX0NPREVfR1JPVVAsIHRvdGFsKSU+JQ0KICBhcnJhbmdlKFlFQVIpDQpjcmltZV90eXBlcw0KYGBgDQoNCg0KDQoNCkludmVzdGlnYXRpbmcgZHJ1ZyByZWxhdGVkIGNyaW1lcyANCmBgYHtyfQ0KZHJ1Z0NyaW1lcyA8LSANCiAgY3JpbWUgJT4lDQogIGdyb3VwX2J5KE9GRkVOU0VfQ09ERV9HUk9VUCAsIFlFQVIsIE1PTlRIKSU+JQ0KICBtdXRhdGUodG90YWwgPSBuKCkpICU+JQ0KICBkaXN0aW5jdChPRkZFTlNFX0NPREVfR1JPVVAsIHRvdGFsKSU+JQ0KICBhcnJhbmdlKFlFQVIsIE1PTlRIKSAlPiUNCiAgZmlsdGVyKE9GRkVOU0VfQ09ERV9HUk9VUCA9PSAiRHJ1ZyBWaW9sYXRpb24iKSAlPiUgDQogIHRyYW5zZm9ybShZRUFSID0gYXMuY2hhcmFjdGVyKFlFQVIpKQ0KDQpkcnVnQ3JpbWVzICANCmBgYA0KDQpgYGB7cn0NCiMgam9pbmluZyB0aGUgb2ZmZW5zZSBjb2RlIHRhYmxlIHdpdGggZHJ1ZyB0YWJsZQ0KDQpjcmltZUpvaW4gPC0NCiAgZHBseXI6OmxlZnRfam9pbihjcmltZSwgb2ZmZW5zZV9jb2RlcywgYnkgPSBjKCJPRkZFTlNFX0NPREUiPSAiQ09ERSIpKSANCmNyaW1lSm9pbg0KDQpgYGANCg0KRHVlIHRvIHRoZSBmYWN0IHRoYXQgdGhlIGxlZ2FsaXphdGlvbiBvZiBtYXJpanVhbmEgaGFzIGJlY29tZSBxdWl0ZSBhIHRyZW5kIHRocm91Z2h0b3V0IHRoZSBVUywgSSB3YXMgY3VyaW91cyBvZiB3aGF0IGVmZmVjdCBpdCBoYWQgaW4gQm9zdG9uLiBCZWxvdyBzaG93cyB0aGUgbnVtYmVyIG9mIGRydWcgcmVsYXRlZCBjaGFyZ2VzIGFuZCB5b3Ugc2VlIGEgc2lnbmlmaWNhbnQgZHJvcCBhcm91bmQgbWlkIDIwMTYgYmVjYXN1ZSBtYXJpanVhbmEgd2FzIGxlZ2FsaXplZCBpbiBKdWx5IG9mIDIwMTYuDQpgYGB7cn0NCg0KY3JpbWVfdHlwZXMgJT4lDQogIGZpbHRlcihPRkZFTlNFX0NPREVfR1JPVVAgJWluJSBjKCJEcnVnIFZpb2xhdGlvbiIpKSAlPiUNCmdncGxvdChhZXMoeCA9IFlFQVIsIHkgPSB0b3RhbCkpICsNCiAgZ2VvbV9saW5lKCkgDQpgYGANCg0KSGVyZSBpcyBhIGdyYXBoIHNob3dpbmcgdGhlIHNpZ25pZmljYW50IGRyb3AgaW4gZHJ1ZyByZWxhdGVkIGNoYXJnZXMgd2l0aCBhbiB4LWludGVyY2VwdCBhdCB0aGUgdGltZSB3aGVuIG1hcmlqdWFuYSB3YXMgbGVnYWxpemVkLg0KYGBge3J9DQoNCmRydWdDcmltZXMgJT4lDQogIGZpbHRlcihZRUFSID09ICIyMDE2IikgJT4lDQogIGdncGxvdChhZXMoeCA9IE1PTlRIICwgeSA9IHRvdGFsKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fc21vb3RoKCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA3KQ0KYGBgDQoNCg0KYGBge3J9DQoNCmNyaW1lICU+JQ0KICBmaWx0ZXIoc3RyX2RldGVjdChPRkZFTlNFX0NPREVfR1JPVVAsICJeRHJ1ZyIpKSAlPiUNCiAgZmlsdGVyKE9DQ1VSUkVEX09OX0RBVEUgPD0geW1kKCIyMDE2LTEyLTMwIikpICU+JQ0KICBmaWx0ZXIoT0NDVVJSRURfT05fREFURSA+IHltZCgiMjAxNi0wMS0wMSIpKSAlPiUNCiAgY291bnQoT0NDVVJSRURfT05fREFURSkgJT4lDQogIGdncGxvdChhZXMoT0NDVVJSRURfT05fREFURSwgbikpKw0KICBnZW9tX2xpbmUoKSANCmBgYA0Kd2hlbiBjb21wYXJpbmcgdGhlIHR3byBncmFwaHMgYWJvdmUgeW91IHNlZSBob3cgdGhlIGxlZ2FsaXphdGlvbiBvZiBtYXJpanVhbmFhIGhhcyBhZmZlY3RlZCB0aGUgZHJ1ZyBjcmltZSBpbiBib3N0b24NCg0KDQoNCg0KDQoNCg0KIyMgUmVsYXRpb25zaGlwIGJldHdlZW4gY3JpbWUgb2NjdXJhbmNlcyBhbmQgdGltZQ0KDQpgYGB7cn0NCmNyaW1lc19ieV95ZWFyIDwtDQogIGNyaW1lICU+JSANCiAgZ3JvdXBfYnkoWUVBUikgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkNCmNyaW1lc19ieV95ZWFyIA0KDQoNCiAgDQpgYGANCmBgYHtyfQ0KI3dpZGUgZm9ybWF0IG9mIG51bWJlciBvZiBjcmltZXMgcGVyIHllYXINCmNyaW1lc19ieV95ZWFyICU+JQ0KICBzcHJlYWQoa2V5PSBZRUFSLCB2YWx1ZSA9IGNvdW50KQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmNyaW1lICU+JSANCiAgDQogIGNvdW50KE9DQ1VSUkVEX09OX0RBVEUpICU+JSANCiAgZ2dwbG90KGFlcyhPQ0NVUlJFRF9PTl9EQVRFLCBuKSkrDQogIGdlb21fbGluZSgpKyANCiAgZ2VvbV9zbW9vdGgoKSsNCiAgbGFicyh0aXRsZSA9IkNyaW1lIFRpbWVsaW5lIDA2LzE0LzE1IC0gMDkvMDMvMTgiLCB4ID0gIlllYXIiLCB5ID0iTnVtYmVyIG9mIENyaW1lcyIsIGNvbG9yID0gInJlZCIpDQpgYGANCg0KQWNjb3JkaW5nIHRvIHRoZSB0aW1lc2VyaWVzIGFib3ZlIGNyaW1lIHRyZW5kcyBmb2xsb3cgYSBjb25zaXN0ZW50IHRyZW5kIHdpdGggZGlwcyBvbiBudW1iZXIgb2Ygb2NjdXJhbmNlcyBhdCB0aGUgZW5kIG9mIGVhY2ggeWVhci4gDQoNCmBgYHtyfQ0KY3JpbWVzX2J5X21vbnRoIDwtDQogIGNyaW1lICU+JSANCiAgZ3JvdXBfYnkoTU9OVEgpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpDQogIA0KYGBgDQoNCg0KYGBge3J9DQpjcmltZSAlPiUNCiAgZ2dwbG90KGFlcyh4PURBWV9PRl9XRUVLKSkgKyANCiAgZ2VvbV9iYXIoZmlsbD0iZ3JheSIsIGFkanVzdCA9IDIpDQpgYGANCg0KDQoNClZpc3VhbGl6YXRpb24gb2YgQ3JpbWVzIGJ5IERheSBvZiBhbmQgVUNUX1BBUlQNCmBgYHtyfQ0KDQogIGdncGxvdChjcmltZSwgYWVzKHg9IERBWV9PRl9XRUVLLCBmaWxsID0gVUNSX1BBUlQpKSArDQogIGdlb21fYmFyKCkgKw0KICB0aGVtZV9idygpICsNCiAgbGFicyggeCA9ICJEYXkgb2YgdGhlIHdlZWsiKSANCiAgDQoNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChjcmltZSxhZXMoIHg9IE1PTlRIICkpICsgDQogIGdlb21fYmFyKCkgKw0KICBnZ3RpdGxlKCJDcmltZXMgYnkgTW9udGgiKQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGNyaW1lLCBhZXMoeD0gWUVBUiwgZmlsbCA9IFVDUl9QQVJUICkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKQ0KYGBgDQoNCmBgYHtyfQ0KY3JpbWUgJT4lDQogIGdyb3VwX2J5KE9GRkVOU0VfQ09ERV9HUk9VUCwgU1RSRUVUKSAlPiUNCiAgc3VtbWFyaXNlKHRvdGFsID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkNCmBgYA0KYGBge3J9DQp0b3BDcmltZXMgPC0gDQogIGNyaW1lICU+JQ0KICBncm91cF9ieShPRkZFTlNFX0NPREVfR1JPVVApJT4lDQogIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSklPiUNCiAgZmlsdGVyKHRvdGFsID49IDE4MDc1KSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkNCiANCiANCg0KYGBgDQoNCg0KYGBge3J9DQpzdHJlZXRzIDwtDQogIGNyaW1lICU+JQ0KICBncm91cF9ieShPRkZFTlNFX0NPREVfR1JPVVAsIFNUUkVFVCkgJT4lDQogIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyh0b3RhbCkpDQpwYXR0ZXJuIDwtICIoU1RSRUVUfCBST0FEfFNUfFNUUkVFVHxSRHxISUdIV0FZfEhXWXxCWVBBU1N8UElLRXxUVVJOUElLRXxBVkV8QVZFTlVFfEJPVUxFVkFSRHxCTFZEKSINCg0KY3JpbWVzT25NYWpvclJvYWQgPC1zdHJlZXRzICU+JQ0KICBmaWx0ZXIoZ3JlcGwocGF0dGVybiwgU1RSRUVUKSkNCmNyaW1lc09uTWFqb3JSb2FkDQpgYGANCmhvdyBtYW55IGNyaW1lcyBoYXBwZW4gb24gZWFjaCBkYXk/DQpgYGB7cn0NCmRheV9jb3VudHMgPC0NCiAgY3JpbWUgJT4lDQogIGdyb3VwX2J5KE9DQ1VSUkVEX09OX0RBVEUsIERBWV9PRl9XRUVLKSAlPiUNCiAgbXV0YXRlKGNvdW50ID0gbigpKSAlPiUNCiAgYXJyYW5nZSgoT0NDVVJSRURfT05fREFURSkpICU+JQ0KICBzZWxlY3QoREFZX09GX1dFRUssT0NDVVJSRURfT05fREFURSxjb3VudCkgJT4lDQogIGRpc3RpbmN0KERBWV9PRl9XRUVLLE9DQ1VSUkVEX09OX0RBVEUsY291bnQpDQpkYXlfY291bnRzDQoNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeSh2aXJpZGlzKQ0KbGlicmFyeShocmJydGhlbWVzKQ0KbGlicmFyeShwbG90bHkpDQp0aW1lIDwtIA0KIyBMb2FkIGRhdGFzZXQgZnJvbSBnaXRodWINCmRmIDwtIGNyaW1lX3R5cGVzICU+JQ0KICBmaWx0ZXIoT0ZGRU5TRV9DT0RFX0dST1VQICVpbiUgYygiTW90b3IgVmVoaWNsZSBBY2NpZGVudCBSZXNwb25zZSIsICJNZWRpY2FsIEFzc2lzdGFuY2UiLCAiSW52ZXN0aWdhdGUgUGVyc29uIiwiTGFyY2VueSIsICJSZXNpZGVudGlhbCBCdXJnbGFyeSIpKQ0KDQoNCiMgR3JhcGgNCnAgPC0gZGYgJT4lIA0KICBnZ3Bsb3QoIGFlcyh4PVlFQVIsIHk9dG90YWwsIGZpbGw9T0ZGRU5TRV9DT0RFX0dST1VQLCB0ZXh0PU9GRkVOU0VfQ09ERV9HUk9VUCkpICsNCiAgICBnZW9tX2FyZWEoICkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGUgPSBUUlVFKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKw0KICAgIGdndGl0bGUoIkhvdyBtYW55IG9mIHRoZSB0b3AgNSBjcmltZXMgb2NjdXIgb3ZlciB0aGUgeWVhcnMiKSArDQogICAgdGhlbWVfaXBzdW0oKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikNCnAgPC0gZ2dwbG90bHkocCwgdG9vbHRpcCA9ICJ0ZXh0IikNCnANCg0KDQpgYGANCg0KDQpgYGB7cn0NCiNjcmltZSBieSBkaXN0cmljdA0KICBjcmltZSAlPiUgDQogIA0KICBncm91cF9ieShESVNUUklDVCkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSklPiUNCiAgbXV0YXRlKHBlcmNlbnQ9IHJvdW5kKGNvdW50L3N1bShjb3VudCkgKiAxMDApKSAlPiUNCiAgYXJyYW5nZShkZXNjKHBlcmNlbnQpKQ0KI2NyaW1lIGJ5IGRpc3RyaWN0IGF0IGNlcnRhaW4gbG9uZyBsYXQsIGhvd2V2ZXIgaXRzIHJlcGVhdGluZyB0aGUgY291bnQNCnh5eiA8LQ0KICBjcmltZSAlPiUgDQogIA0KICBncm91cF9ieShESVNUUklDVCkgJT4lDQogIG11dGF0ZShjb3VudCA9IG4oKSklPiUNCiAgbXV0YXRlKHBlcmNlbnQ9IHJvdW5kKGNvdW50L3N1bShjb3VudCkgKiAxMDApKSAlPiUNCiAgI2FycmFuZ2UoZGVzYyhwZXJjZW50KSklPiUNCiAgc2VsZWN0KERJU1RSSUNULCBjb3VudCxMYXQsIExvbmcpDQogDQp4eXogJT4lDQogIGdyb3VwX2J5KERJU1RSSUNULCBjb3VudCklPiUNCiAgZGlzdGluY3QoKSAlPiUNCiAgZmlsdGVyKHN0cl9kZXRlY3QoTGF0LCAiIikpDQoNCiAgDQoNCmBgYA0KDQoNCiMjIE1BUFBJTkcgDQpHaXZlbiB0aGF0IHRoaXMgZGF0YXNldCBjb250YWluZWQgbG9jYXRpb24gZGF0YSBwZXJ0YWluaW5nIHRvIGVhY2ggcmVwb3J0ZWQgaW5jaWRlbnQgSSB0aG91Z2h0IHRoYXQgdGhlIGxlYWZsZXQgcGFja2FnZSB3b3VsZCBiZSB2aXRhbCBpbiBpbGx1c3RyYXRpbmcgd2hlcmUgY3JpbWVzIG9jY3VyZWQgdGhyb3VnaG91dCBCb3N0b24uDQoNClRoZSBmaXJzdCBtYXAgdGhhdCBJIHVzZWQgd2FzIG9uZSB0aGF0IHNob3dzIHdoZXJlIG9uIGEgbWFwIHRoZSBCb3N0b24gaXMgbG9jYXRlZC4NCmBgYHtyfQ0KbGVhZmxldCgpICU+JQ0KICBhZGRUaWxlcygpICU+JQ0KICBhZGRNYXJrZXJzKGxuZz0tNzEuMDU3LCBsYXQ9NDIuMzYxLCBwb3B1cD0iQm9zdG9uLCBNQSIpICU+JQ0KICBzZXRWaWV3KGxuZz0tNzEuMDU3LCBsYXQ9NDIuMzYxLCB6b29tID0gMTApDQpgYGANCg0KDQpraWRuYXBwaW5nIGRpc3RhbmNlIGNhbGN1bGF0aW9ucw0KYGBge3J9DQpraWRuYXAgPC0NCiAgY3JpbWUgJT4lDQogIHNlbGVjdChJTkNJREVOVF9OVU1CRVIsU1RSRUVULE9GRkVOU0VfQ09ERSxPRkZFTlNFX0NPREVfR1JPVVAsIE9GRkVOU0VfREVTQ1JJUFRJT04sIExvbmcsIExhdCwgTG9jYXRpb24sIE9DQ1VSUkVEX09OX0RBVEUpJT4lDQogIGZpbHRlcihPRkZFTlNFX0RFU0NSSVBUSU9OICVpbiUgYygiTUlTU0lORyBQRVJTT04iKSkgJT4lDQogIGFycmFuZ2UoT0NDVVJSRURfT05fREFURSkNCg0KDQpmb3VuZCA8LQ0KICBjcmltZSAlPiUNCiAgc2VsZWN0KElOQ0lERU5UX05VTUJFUixTVFJFRVQsT0ZGRU5TRV9DT0RFLE9GRkVOU0VfQ09ERV9HUk9VUCwgT0ZGRU5TRV9ERVNDUklQVElPTiwgTG9uZywgTGF0LCBMb2NhdGlvbiwgT0NDVVJSRURfT05fREFURSklPiUNCiAgZmlsdGVyKE9GRkVOU0VfREVTQ1JJUFRJT04gJWluJSBjKCJNSVNTSU5HIFBFUlNPTiAtIExPQ0FURUQiKSkgJT4lDQogIGFycmFuZ2UoT0NDVVJSRURfT05fREFURSkgJT4lDQogIHJlbmFtZShMYXQyID0gTGF0LCBMb25nMiA9IExvbmcgKQ0KDQpgYGANCg0KDQoNCg0KYGBge3J9DQpraWRuYXBQYWlycyA8LQ0KICBtZXJnZShkYXRhLmZyYW1lKGtpZG5hcCwgcm93Lm5hbWVzID0gTlVMTCksIGRhdGEuZnJhbWUoZm91bmQsIHJvdy5uYW1lcyA9IE5VTEwpLCBieSA9IDAsIGFsbCA9IFRSVUUpWy0xXQ0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KY3JpbWUgJT4lDQogIHNlbGVjdChJTkNJREVOVF9OVU1CRVIsU1RSRUVULE9GRkVOU0VfQ09ERSxPRkZFTlNFX0NPREVfR1JPVVAsIE9GRkVOU0VfREVTQ1JJUFRJT04sIExvbmcsIExhdCwgTG9jYXRpb24sIE9DQ1VSUkVEX09OX0RBVEUpJT4lDQogICNmaWx0ZXIoT0ZGRU5TRV9ERVNDUklQVElPTiAlaW4lIGMoIk1JU1NJTkcgUEVSU09OIikpJT4lDQogIG11dGF0ZShtaXNzaW5nID0gT0ZGRU5TRV9DT0RFX0dST1VQID09ICJNaXNzaW5nIFBlcnNvbiBSZXBvcnRlZCIpICU+JQ0KICBmaWx0ZXIoT0ZGRU5TRV9DT0RFX0dST1VQICVpbiUgYygiTWlzc2luZyBQZXJzb24gUmVwb3J0ZWQiLCAiTWlzc2luZyBQZXJzb24gTG9jYXRlZCIpKSU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBPRkZFTlNFX0NPREVfR1JPVVAsIGZpbGwgPSBtaXNzaW5nKSkgKw0KICBnZW9tX2JhcigpDQoNCmBgYA0KVGhpcyBiYXJwbG90IGlsbHVzdHJhdGVzIHRoYXQgdGhlcmUgYXJlIG1vcmUgbWlzc2luZyBwZW9wbGUgbG9jYXRlZCB0aGFuIGFyZSByZXBvcnRlZCB3aGljaCBpcyBxdWl0ZSBpbnRlcnJlc3RpbmcgZ2l2ZW4gdGhlIHBvbGljZSBhcmVuJ3QgYXdhcmUgb2YgbWFueSBvZiB0aGVzZSBtaXNzaW5nIHBlcnNvbnMuDQoNCkJlbG93IHNob3dzIHRoZSBsb2NhdGlvbnMgb2YgYWxsIHJlcG90ZWQga2lkbmFwcGluZ3MvbG9jYXRpb25zIG9mIHdoZXJlIHZpY3RpbXMgd2VyZSBmb3VuZC4gVGhlIHJlZCBjaXJjbGVzIGluZGljYXRlIHRoZSBraWRuYXBwaW5nIGxvY2F0aW9uIGFuZCB0aGUgYmx1ZSBpbmRpY2F0ZXMgdGhlIGxvY2F0aW9ucyB3aGVyZSBtaXNzaW5nIHBlb3BsZSB3ZXJlIGZvdW5kDQpgYGB7cn0NCmtpZG5hcE1hcCA8LQ0KICBsZWFmbGV0KGtpZG5hcFBhaXJzKSU+JQ0KICBhZGRUaWxlcygpICU+JQ0KICBhZGRDaXJjbGVzKGxhdCA9IH5MYXQsIGxuZyA9IH5Mb25nLCByYWRpdXMgPSAyLCBjb2xvcj0gInJlZCIpJT4lDQogIGFkZENpcmNsZXMobGF0ID0gfkxhdDIsIGxuZyA9IH5Mb25nMiwgcmFkaXVzID0gMiwgY29sb3I9ICJibHVlIikgJT4lDQogIHNldFZpZXcoLTcxLjA1LCA0Mi4zNiwgem9vbSA9IDEyKQ0Ka2lkbmFwTWFwDQoNCmBgYA0KDQoNCkxvY2F0aW9ucyBvZiBhbGwgaG9taWNpZGVzIGluIGJvc3Rvbg0KYGBge3J9DQpjcmltZUpvaW4gJT4lDQogIGdyb3VwX2J5KE9GRkVOU0VfQ09ERV9HUk9VUCxEQVlfT0ZfV0VFSywgSE9VUiwgVUNSX1BBUlQgKSU+JQ0KICBmaWx0ZXIoT0ZGRU5TRV9DT0RFX0dST1VQICVpbiUgYygiSG9taWNpZGUiKSkgJT4lIA0KICBsZWFmbGV0KCkgJT4lDQogIGFkZFRpbGVzKCkgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMocmFkaXVzID0gMiwgY29sb3IgPSAicmVkIikgJT4lDQogIHNldFZpZXcoLTcxLjA1LCA0Mi4zNiwgem9vbSA9IDEyKQ0KICANCmBgYA0KDQoNCiMjIENvbmNsdXNpb24NCkFmdGVyIGFuYWx5emluZyB0aGUgQm9zdG9uIENyaW1lIERhdGEgU2V0LCB3ZSBjYW4gY2xlYXJseSBzZWUgdGhlIHRyZW5kcyBhbmQgcmVsYXRpb25zIGJldHdlZW4gdGhlIHR5cGVzIG9mIGNyaW1lcywgbG9jYXRpb24gYW5kIHRoZSB0aW1lIG9mIHRoZSBjcmltZS4gU29tZSBvZiB0aGUgbm90YWJsZSB0YWtlYXdheXMgZnJvbSB0aGUgYW5hbHlzaXMgYXJlIHRoYXQgbW9zdCBjcmltZXMgaW4gQm9zdG9uIG9jY3VycmVkIG9uIGEgV2VkbmVzZGF5IGFuZCBkdXJpbmcgdGhlIG1vbnRoIG9mIE9jdG9iZXIuIFRoZSBtb3N0IGFmZmVjdGVkIGRpc3RyaWN0cyBpbiBCb3N0b24gYXJlIERvcmNoZXN0ZXIoQzExKSwgU291dGgtRW5kLCBSb3hidXJ5KEIyKS4gSXQgY2FuIGJlIG5vdGVkIHRoYXQgdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIHRoZSBjcmltZXMgd2VyZSByZXBvcnRlZCBkdXJpbmcgc3VtbWVyIG1vbnRocyBvZiBKdWx5IGFuZCBBdWd1ZXN0LiBNb3Rvci1WZWhpY2xlIGFjY2lkZW50IHJlc3BvbnNlIHdlcmUgdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIHJlcG9ydCByZWdpc3RlcmVkIHdpdGggdGhlIEJvc3RvbiBQb2xpY2Uu